package io.kiki.sba.registry.common.model;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.google.common.base.Objects;
import lombok.Getter;
import lombok.Setter;

import java.io.*;
import java.util.Arrays;

@Setter
@Getter
public class ServerData implements Serializable {

    private static final long serialVersionUID = 1L;

    private static final int SERIALIZED_BY_JAVA = 1;
    /**
     * Null for locally instantiated, otherwise for internalized
     */

    private byte[] bytes;
    /**
     * Only available if bytes != null
     */

    private int serialization;
    /**
     * Actual object, lazy deserialized
     */
    private Object object;


    public ServerData(Object object) {
        this.object = object;
    }


    public ServerData(byte[] bytes) {
        this.bytes = bytes;
        this.serialization = SERIALIZED_BY_JAVA;
    }

    public static byte[] getBytes(Object object) {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = null;
        try {
            objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
            objectOutputStream.writeObject(object);
        } catch (IOException ioe) {
            throw new RuntimeException(ioe);
        } finally {
            try {
                if (null != objectOutputStream) {
                    objectOutputStream.close();
                }
            } catch (IOException ioe) {
                throw new RuntimeException(ioe);
            }
        }
        return byteArrayOutputStream.toByteArray();
    }

    /**
     * Is in bytes boolean.
     */
    @JsonIgnore
    public boolean isInBytes() {
        return bytes != null;
    }


    /**
     * transfer bytes to object
     *
     * @return Object object
     * @throws IOException            the io exception
     * @throws ClassNotFoundException the class not found exception
     */
    public Object extract() throws IOException, ClassNotFoundException {
        if (object == null && isInBytes()) {
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
            if (serialization != SERIALIZED_BY_JAVA) {
                throw new IOException("Unsupported serialization type: " + serialization);
            }
            ServerDataBoxInputStream serverDataBoxInputStream = null;
            try {
                serverDataBoxInputStream = new ServerDataBoxInputStream(byteArrayInputStream);
                object = serverDataBoxInputStream.readObject();
            } finally {
                if (serverDataBoxInputStream != null) {
                    serverDataBoxInputStream.close();
                }
            }
        }

        return object;
    }

    /**
     * change object to bytes
     *
     * @return NSwizzle swizzle
     */
    public ServerData object2bytes() {
        if (!isInBytes()) {
            bytes = getBytes(object);
            serialization = SERIALIZED_BY_JAVA;
        }
        return this;
    }

    private void readObject(ObjectInputStream objectInputStream) throws IOException, ClassNotFoundException {
        serialization = objectInputStream.readByte(); // Read serialization type
        int size = objectInputStream.readInt(); // Read byte stream size
        bytes = new byte[size];
        objectInputStream.readFully(bytes); // Read the byte stream
    }

    private void writeObject(ObjectOutputStream objectOutputStream) throws IOException {
        if (isInBytes()) {
            objectOutputStream.writeByte(serialization); // Write serialization type
            objectOutputStream.writeInt(bytes.length); // Write byte stream size
            objectOutputStream.write(bytes); // Write the byte stream
        } else {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            ObjectOutputStream objectOutputStream1 = new ObjectOutputStream(byteArrayOutputStream);
            try {
                objectOutputStream1.writeObject(object);
            } finally {
                try {
                    objectOutputStream1.close();
                } catch (IOException ioe) {
                    throw new RuntimeException(ioe);
                }
            }
            objectOutputStream.writeByte(SERIALIZED_BY_JAVA); // Write serialization type
            objectOutputStream.writeInt(byteArrayOutputStream.size()); // Write byte stream size
            objectOutputStream.write(byteArrayOutputStream.toByteArray()); // Write the byte stream
        }
    }

    public int byteSize() {
        final byte[] bytes1 = bytes;
        return bytes1 != null ? bytes1.length : 0;
    }


    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        ServerData that = (ServerData) o;
        return Arrays.equals(bytes, that.bytes) && Objects.equal(object, that.object);
    }
}
